/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2005 INRIA
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
 */

#include "ipv4-texcp-table-entry.h"
#include "ns3/assert.h"
#include "ns3/simulator.h"
#include "ns3/trace-source-accessor.h"

namespace ns3 {

namespace texcp_routing {

TypeId 
Ipv4TexcpTableEntry::GetTypeId (void) 
{
   static TypeId tid = TypeId ("ns3::texcp_routing::Ipv4TexcpTableEntry")
    .SetParent<Object> ()
    .AddConstructor<Ipv4TexcpTableEntry> ()
     .AddAttribute ("AlphaLoss", "Weight for weighted exponential average of loss.",
                   DoubleValue (0.2),
                   MakeDoubleAccessor (&Ipv4TexcpTableEntry::m_alpha),
                   MakeDoubleChecker<double> ())
    .AddAttribute ("MinLoss", "Lower bound for loss.",
                   DoubleValue (0.0),
                   MakeDoubleAccessor (&Ipv4TexcpTableEntry::m_minLoss),
                   MakeDoubleChecker<double> ())
    .AddTraceSource ("Loss", 
                  "Ratio of loss on this prefix/path tuple",
                   MakeTraceSourceAccessor (&Ipv4TexcpTableEntry::m_loss))
            ;
  return tid;
}

Ipv4TexcpTableEntry::Ipv4TexcpTableEntry ()
{}

Ipv4TexcpTableEntry::Ipv4TexcpTableEntry (Ipv4TexcpTableEntry const &route)
  : m_dest (route.m_dest),
    m_destNetworkMask (route.m_destNetworkMask),
    m_interface (route.m_interface),
    m_iePair (route.m_iePair),
    m_probe(route.m_probe),
    m_tokenCounter(0)
{}

Ipv4TexcpTableEntry::Ipv4TexcpTableEntry (Ipv4TexcpTableEntry const *route)
  : m_dest (route->m_dest),
    m_destNetworkMask (route->m_destNetworkMask),
    m_interface (route->m_interface),
    m_iePair (route -> m_iePair),
    m_probe(route -> m_probe),
    m_tokenCounter(0)
{}
  
Ipv4TexcpTableEntry::Ipv4TexcpTableEntry (Ipv4Address network,
                      Ipv4Mask networkMask,
                      uint32_t interface,
                      struct IEPair s)
//,
//                      Ptr<TexcpPathState> texcp)
  : m_dest (network),
    m_destNetworkMask (networkMask),
    m_interface (interface),
    m_iePair (s),
    m_tokenCounter(0)
{

  m_probe = Create<Packet> ();
  TexcpTag tag;
  TexcpHeader probe;
  tag.SetInterfaceIdx(interface);
  probe.SetInterfaceIdx(interface);
  probe.SetIEPair(s);
  m_probe->AddHeader(probe);
  m_probe->AddPacketTag(tag);
  m_alpha = 0.2;
  //m_pathShaperId =  next_path_shaper_id(); 
  ClearCount();
}

Ipv4Address 
Ipv4TexcpTableEntry::GetDestNetwork (void) const
{
  return m_dest;
}

Ipv4Mask 
Ipv4TexcpTableEntry::GetDestNetworkMask (void) const
{
  return m_destNetworkMask;
}

uint32_t
Ipv4TexcpTableEntry::GetInterface (void) const
{
  return m_interface;
}

struct IEPair 
Ipv4TexcpTableEntry::GetIEPair (void) const
{
  return m_iePair;
}

Ptr<Packet>
Ipv4TexcpTableEntry::GetProbe (void)
{
  return m_probe;
}

Ipv4TexcpTableEntry 
Ipv4TexcpTableEntry::CreateNetworkRouteTo (Ipv4Address network, 
				 Ipv4Mask networkMask, 
				 uint32_t interface,
                                 struct IEPair s)
{
  return Ipv4TexcpTableEntry (network, networkMask, 
                    interface, s);
}

std::ostream& operator<< (std::ostream& os, Ipv4TexcpTableEntry const& route)
{
  os << "network=" << route.GetDestNetwork () <<
    ", mask=" << route.GetDestNetworkMask () <<
    ",out=" << route.GetInterface ();
  return os;
}

/*std::ostream& operator<< (std::ostream& os, TexcpPathState const& stat)
{
  // *for ( int i=0; i < TexcpPathState::MAX_CODEPOINT; i++ )
      os << i << " " << stat.Get(i) << '\n';* /
  return os;
}*/
void
Ipv4TexcpTableEntry::UpdateProbe (TexcpHeader probe)
{
  m_pathUtilization = (probe.GetPathUtilization());//*0.25 + m_pathUtilization*0.75;
  double oldPathRate = static_cast<double>(m_allowedPathRate);
  double newPathRate = oldPathRate + probe.GetPositiveFeedback() - (oldPathRate*probe.GetNegativeFeedback());
  m_allowedPathRate = static_cast<uint64_t>(newPathRate);

 // m_allowedPathRate = m_allowedPathRate + probe.GetPositiveFeedback() - (m_allowedPathRate*probe.GetNegativeFeedback());
  m_pathLoss = probe.GetPathLoss();
}

double 
Ipv4TexcpTableEntry::UpdateTokenCounter (uint32_t size)
{
  //std::cout << " Token update \n";
  m_tokenCounter = m_tokenCounter + GetWeight()*(static_cast<double>(size));
  return m_tokenCounter;
}

void 
Ipv4TexcpTableEntry::SelectPath (uint32_t size)
{
  //std::cout << " Select Path \n";
  m_tokenCounter-=size;
}

//int Ipv4TexcpTableEntry::nextPathId = 0;

 void 
Ipv4TexcpTableEntry::UpdateReflex (int codepoint, const unsigned int value)
{
  NS_ASSERT ( codepoint < MAX_CODEPOINT );
  //std::cout << "Update Reflex " << codepoint << "\n";
  m_count[codepoint] += value;
  m_total += value;
}

void
Ipv4TexcpTableEntry::CalculateLoss ()
{
  double loss = 0;

  if( m_total ) {
    loss = m_count[LE];
    loss /= m_total;
  }
  //m_totalBytes +=m_total;
  //m_totalLostBytes += m_count[LE];
  ClearCount();

  if( loss < m_minLoss ) {
    loss = 0;
  }
  m_loss *= (1.0-m_alpha);
  double addedloss = loss*m_alpha;
  m_loss += addedloss;
}

void
Ipv4TexcpTableEntry::ClearCount ()
{
  for (int i = 0; i < MAX_CODEPOINT; i++)
    m_count[i] = 0;
  m_total = 0;
}
 
double 
Ipv4TexcpTableEntry::GetLoss () const
{
  return m_loss;
}                     
}



} //namespace ns3
